Osvojte si súbory deklarácií TypeScriptu (.d.ts) a odomknite typovú bezpečnosť a automatické dopĺňanie pre akúkoľvek knižnicu JavaScriptu. Naučte sa používať @types, vytvárať vlastné definície a pracovať s kódom tretích strán ako profesionál.
Odomknutie ekosystému JavaScriptu: Hlboký ponor do súborov deklarácií TypeScriptu
TypeScript spôsobil revolúciu v modernom vývoji webu tým, že priniesol statické typovanie do dynamického sveta JavaScriptu. Táto typová bezpečnosť poskytuje neuveriteľné výhody: zachytávanie chýb v čase kompilácie, umožnenie výkonného automatického dopĺňania editorom a výrazné zvýšenie udržiavateľnosti rozsiahlych kódových báz. Hlavná výzva však nastáva, keď chceme využiť rozsiahly ekosystém existujúcich knižníc JavaScriptu – z ktorých väčšina nebola napísaná v TypeScripte. Ako náš prísne typovaný kód TypeScriptu rozumie tvarom, funkciám a premenným z netypovanej knižnice JavaScriptu?
Odpoveď spočíva v Súboroch deklarácií TypeScriptu. Tieto súbory, identifikovateľné ich príponou .d.ts, sú základným mostom medzi svetmi TypeScriptu a JavaScriptu. Fungujú ako plán alebo API kontrakt, ktorý popisuje typy knižnice tretej strany bez toho, aby obsahoval akúkoľvek z jej skutočných implementácií. V tomto komplexnom sprievodcovi preskúmame všetko, čo potrebujete vedieť, aby ste s istotou spravovali definície typov pre akúkoľvek knižnicu JavaScriptu vo vašich projektoch TypeScriptu.
Čo presne sú súbory deklarácií TypeScriptu?
Predstavte si, že ste si najali dodávateľa, ktorý hovorí iba iným jazykom. Aby ste s ním efektívne spolupracovali, potrebovali by ste prekladateľa alebo podrobný súbor pokynov v jazyku, ktorému obaja rozumiete. Deklaračný súbor slúži presne na tento účel pre kompilátor TypeScriptu (dodávateľa).
Súbor .d.ts obsahuje iba informácie o type. Obsahuje:
- Signatúry pre funkcie a metódy (typy parametrov, návratové typy).
- Definície pre premenné a ich typy.
- Rozhrania a aliasy typov pre komplexné objekty.
- Definície tried, vrátane ich vlastností a metód.
- Štruktúry priestoru mien a modulov.
Zásadne, tieto súbory neobsahujú žiadny spustiteľný kód. Sú určené výlučne pre statickú analýzu. Keď importujete knižnicu JavaScriptu, ako napríklad Lodash do svojho projektu TypeScriptu, kompilátor hľadá zodpovedajúci deklaračný súbor. Ak ho nájde, môže overiť váš kód, poskytnúť inteligentné automatické dopĺňanie a zabezpečiť, že knižnicu používate správne. Ak ho nenájde, vyvolá chybu ako: Could not find a declaration file for module 'lodash'.
Prečo sú deklaračné súbory pre profesionálny vývoj nevyhnutné
Používanie knižníc JavaScriptu bez správnych definícií typov v projekte TypeScriptu podkopáva samotný dôvod používania TypeScriptu. Zvážme jednoduchý scenár pomocou populárnej pomocnej knižnice, Lodash.Svet bez definícií typov
Bez deklaračného súboru TypeScript nemá predstavu o tom, čo je lodash alebo čo obsahuje. Aby ste kód vôbec preložili, môžete byť v pokušení použiť rýchlu opravu, ako je táto:
const _: any = require('lodash');
const users = [{ 'user': 'barney' }, { 'user': 'fred' }];
// Autocomplete? No help here.
// Type checking? No. Is 'username' the correct property?
// The compiler allows this, but it might fail at runtime.
_.find(users, { username: 'fred' });
V tomto prípade má premenná _ typ any. To v podstate hovorí TypeScriptu: "Nekontrolujte nič, čo sa týka tejto premennej." Stratíte všetky výhody: žiadne automatické dopĺňanie, žiadna kontrola typov argumentov a žiadna istota o návratovom type. Toto je živná pôda pre chyby za behu.
Svet s definíciami typov
Teraz sa pozrime, čo sa stane, keď poskytneme potrebný deklaračný súbor. Po inštalácii typov (ktoré si prejdeme ďalej) sa zážitok transformuje:
import _ from 'lodash';
interface User {
user: string;
active?: boolean;
}
const users: User[] = [{ 'user': 'barney' }, { 'user': 'fred' }];
// 1. Editor provides autocompletion for 'find' and other lodash functions.
// 2. Hovering over 'find' shows its full signature and documentation.
// 3. TypeScript sees that `users` is an array of `User` objects.
// 4. TypeScript knows the predicate for `find` on `User[]` should involve `user` or `active`.
// CORRECT: TypeScript is happy.
const fred = _.find(users, { user: 'fred' });
// ERROR: TypeScript catches the mistake!
// Property 'username' does not exist on type 'User'.
const betty = _.find(users, { username: 'betty' });
Rozdiel je ako deň a noc. Získavame plnú typovú bezpečnosť, vynikajúci vývojársky zážitok prostredníctvom nástrojov a dramatické zníženie potenciálnych chýb. Toto je profesionálny štandard pre prácu s TypeScriptom.
Hierarchia hľadania definícií typov
Takže, ako získate tieto magické súbory .d.ts pre vaše obľúbené knižnice? Existuje dobre zavedený proces, ktorý pokrýva veľkú väčšinu scenárov.
Krok 1: Skontrolujte, či knižnica zoskupuje svoje vlastné typy
Najlepší scenár je, keď je knižnica napísaná v TypeScripte alebo jej správcovia poskytujú oficiálne deklaračné súbory v rámci toho istého balíka. Toto sa stáva čoraz bežnejším pre moderné, dobre udržiavané projekty.
Ako skontrolovať:
- Nainštalujte knižnicu ako zvyčajne:
npm install axios - Pozrite sa do priečinka knižnice v
node_modules/axios. Vidíte nejaké súbory.d.ts? - Skontrolujte súbor
package.jsonknižnice, či neobsahuje pole"types"alebo"typings". Toto pole ukazuje priamo na hlavný deklaračný súbor. Napríklad,package.jsonaplikácie Axios obsahuje:"types": "index.d.ts".
Ak sú tieto podmienky splnené, ste hotoví! TypeScript automaticky nájde a použije tieto zoskupené typy. Nie je potrebná žiadna ďalšia akcia.
Krok 2: Projekt DefinitelyTyped (@types)
Pre tisíce knižníc JavaScriptu, ktoré nezoskupujú svoje vlastné typy, vytvorila globálna komunita TypeScriptu neuveriteľný zdroj: DefinitelyTyped.
DefinitelyTyped je centralizované, komunitou spravované úložisko na GitHub, ktoré hostí vysokokvalitné deklaračné súbory pre rozsiahly počet balíkov JavaScriptu. Tieto definície sú publikované v registri npm v rozsahu @types.
Ako ho používať:
Ak knižnica ako lodash nezoskupuje svoje vlastné typy, jednoducho nainštalujete jej zodpovedajúci balík @types ako vývojovú závislosť:
npm install --save-dev @types/lodash
Konvencia pomenúvania je jednoduchá a predvídateľná: pre balík s názvom package-name budú jeho typy takmer vždy na @types/package-name. Môžete vyhľadať dostupné typy na webovej stránke npm alebo priamo v úložisku DefinitelyTyped.
Prečo --save-dev? Deklaračné súbory sú potrebné iba počas vývoja a kompilácie. Neobsahujú žiadny kód za behu, takže by nemali byť zahrnuté vo vašom konečnom produkčnom balíku. Inštalácia ako devDependency zabezpečuje toto oddelenie.
Krok 3: Keď neexistujú žiadne typy – písanie vlastných
Čo ak používate staršiu, okrajovú alebo internú súkromnú knižnicu, ktorá nezoskupuje typy a nie je na DefinitelyTyped? V tomto prípade si musíte vyhrnúť rukávy a vytvoriť si vlastný deklaračný súbor. Aj keď to môže znieť zastrašujúco, môžete začať jednoducho a podľa potreby pridať viac detailov.
Rýchla oprava: Skrátená deklarácia okolitého modulu
Niekedy potrebujete, aby sa váš projekt preložil bez chýb, kým si nevymyslíte správnu stratégiu typovania. Môžete vytvoriť súbor vo svojom projekte (napr. declarations.d.ts alebo types/global.d.ts) a pridať skrátenú deklaráciu:
// in a .d.ts file
declare module 'some-untyped-library';
Toto hovorí TypeScriptu: "Ver mi, modul s názvom 'some-untyped-library' existuje. Jednoducho považujte všetko, čo je z neho importované, za typ any." Tým sa umlčí chyba kompilátora, ale ako sme už diskutovali, obetuje sa všetka typová bezpečnosť pre túto knižnicu. Je to dočasná záplata, nie dlhodobé riešenie.
Vytvorenie základného vlastného deklaračného súboru
Lepší prístup je začať definovať typy pre časti knižnice, ktoré skutočne používate. Povedzme, že máme jednoduchú knižnicu s názvom `string-utils`, ktorá exportuje jednu funkciu.
// In node_modules/string-utils/index.js
module.exports.capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
Môžeme vytvoriť súbor string-utils.d.ts v špecializovanom adresári `types` v koreňovom adresári nášho projektu.
// In my-project/types/string-utils.d.ts
declare module 'string-utils' {
export function capitalize(str: string): string;
// You could add other function definitions here as you use them
// export function slugify(str: string): string;
}
Teraz musíme povedať TypeScriptu, kde má hľadať naše vlastné definície typov. Urobíme to v tsconfig.json:
{
"compilerOptions": {
// ... other options
"baseUrl": ".",
"paths": {
"*": ["types/*"]
}
}
}
S týmto nastavením, keď import { capitalize } from 'string-utils', TypeScript nájde váš vlastný deklaračný súbor a poskytne typovú bezpečnosť, ktorú ste definovali. Tento súbor môžete postupne rozširovať, keď budete používať viac funkcií knižnice.
Hlbší ponor: Vytváranie deklaračných súborov
Poďme preskúmať niektoré pokročilejšie koncepty, s ktorými sa stretnete pri písaní alebo čítaní deklaračných súborov.
Deklarovanie rôznych druhov exportov
Moduly JavaScriptu môžu exportovať veci rôznymi spôsobmi. Váš deklaračný súbor musí zodpovedať štruktúre exportu knižnice.
- Pomenované exporty: Toto je najbežnejšie. Videli sme to vyššie s `export function capitalize(...)`. Môžete tiež exportovať konštanty, rozhrania a triedy.
- Predvolený export: Pre knižnice, ktoré používajú `export default`.
- UMD Globals: Pre staršie knižnice navrhnuté na prácu v prehliadačoch prostredníctvom značky
<script>sa často pripájajú ku globálnemu objektu `window`. Môžete deklarovať tieto globálne premenné. - `export =` a `import = require()`: Táto syntax je určená pre staršie moduly CommonJS, ktoré používajú `module.exports = ...`. Napríklad, ak knižnica robí `module.exports = myClass;`.
declare module 'my-lib' {
export const version: string;
export interface Options { retries: number; }
export function doSomething(options: Options): Promise
declare module 'my-default-lib' {
// For a function default export
export default function myCoolFunction(): void;
// For an object default export
// const myLib = { name: 'lib', version: '1.0' };
// export default myLib;
}
// Declares a global variable '$' of a certain type
declare var $: JQueryStatic;
// in my-class.d.ts
declare class MyClass { constructor(name: string); }
export = MyClass;
// in your app.ts
import MyClass = require('my-class');
const instance = new MyClass('test');
Aj keď je menej bežné s modernými ES modulmi, je to kritické pre kompatibilitu s mnohými staršími, ale stále široko používanými balíkmi Node.js.
Rozšírenie modulu: Rozšírenie existujúcich typov
Jednou z najvýkonnejších funkcií je rozšírenie modulu (známe aj ako zlúčenie deklarácií). To vám umožňuje pridávať vlastnosti k existujúcim rozhraniam definovaným v deklaračnom súbore iného balíka. To je mimoriadne užitočné pre knižnice s architektúrou zásuvných modulov, ako napríklad Express alebo Fastify.
Predstavte si, že používate middleware v Express, ktorý pridáva vlastnosť `user` do objektu `Request`. Bez rozšírenia by sa TypeScript sťažoval, že `user` neexistuje na `Request`.
Tu je postup, ako môžete povedať TypeScriptu o tejto novej vlastnosti:
// in your types/express.d.ts file
// We must import the original type to augment it
import { UserProfile } from './auth'; // Assuming you have a UserProfile type
// Tell TypeScript we're augmenting the 'express-serve-static-core' module
declare module 'express-serve-static-core' {
// Target the 'Request' interface inside that module
interface Request {
// Add our custom property
user?: UserProfile;
}
}
Teraz, v celej vašej aplikácii, bude objekt Express `Request` správne typovaný s voliteľnou vlastnosťou `user` a získate plnú typovú bezpečnosť a automatické dopĺňanie.
Smernice s tromi lomkami
Niekedy môžete vidieť komentáre v hornej časti súborov .d.ts, ktoré začínajú tromi lomkami (///). Toto sú smernice s tromi lomkami, ktoré fungujú ako inštrukcie kompilátora.
/// <reference types="..." />: Toto je najbežnejšie. Explicitne zahŕňa definície typov iného balíka ako závislosť. Napríklad, typy pre zásuvný modul WebdriverIO môžu obsahovať/// <reference types="webdriverio" />, pretože jeho vlastné typy závisia od základných typov WebdriverIO./// <reference path="..." />: Používa sa na deklarovanie závislosti od iného súboru v rámci toho istého projektu. Je to staršia syntax, ktorá je do značnej miery nahradená importmi modulov ES.
Osvedčené postupy pre správu deklaračných súborov
- Preferujte zoskupené typy: Pri výbere medzi knižnicami uprednostňujte tie, ktoré sú napísané v TypeScripte alebo zoskupujú svoje vlastné oficiálne definície typov. Signalizuje to záväzok voči ekosystému TypeScriptu.
- Udržujte
@typesvdevDependencies: Vždy inštalujte balíky@typespomocou--save-devalebo-D. Nie sú potrebné pre váš produkčný kód. - Zosúlaďte verzie: Bežným zdrojom chýb je nesúlad medzi verziou knižnice a jej verziou
@types. Významný nárast verzie v knižnici (napr. z v2 na v3) bude pravdepodobne mať zásadné zmeny v jej API, ktoré sa musia odraziť v balíku@types. Pokúste sa ich synchronizovať. - Použite
tsconfig.jsonna kontrolu: Možnosti kompilátoratypeRootsatypesvo vašomtsconfig.jsonvám môžu poskytnúť jemnú kontrolu nad tým, kde TypeScript hľadá deklaračné súbory.typeRootspovie kompilátoru, ktoré priečinky má skontrolovať (predvolene je to./node_modules/@types) atypesvám umožňuje explicitne uviesť, ktoré typové balíky sa majú zahrnúť. - Prispejte späť: Ak napíšete komplexný deklaračný súbor pre knižnicu, ktorá ho nemá, zvážte jeho prispenie do projektu DefinitelyTyped. Toto je fantastický spôsob, ako vrátiť niečo globálnej komunite vývojárov a pomôcť tisíckam ďalších.
Záver: Nepovšimnutí hrdinovia typovej bezpečnosti
Súbory deklarácií TypeScriptu sú nepovšimnutí hrdinovia, ktorí umožňujú bezproblémovú integráciu dynamického, rozsiahleho sveta JavaScriptu do robustného vývojového prostredia s typovou bezpečnosťou. Sú kritickým prepojením, ktoré posilňuje naše nástroje, zabraňuje nespočetným chybám a robí naše kódové základne odolnejšími a sebadeklarujúcimi.
Pochopením toho, ako nájsť, používať a dokonca vytvárať vlastné súbory .d.ts, neopravujete iba chybu kompilátora – zlepšujete celý svoj vývojový pracovný postup. Odomykáte plný potenciál TypeScriptu aj bohatého ekosystému knižníc JavaScriptu, čím vytvárate silnú synergiu, ktorá vedie k lepšiemu a spoľahlivejšiemu softvéru pre globálne publikum.